from umqtt.simple import MQTTClient
import machine
import dht
import network
import time
import neopixel
import json
from machine import ADC,Pin,DAC,PWM,I2C,UART
from time import sleep_ms, ticks_ms, sleep 
from i2c_lcd import I2cLcd
from mfrc522_i2c import mfrc522

# WiFi设置
ssid = 'Jubjub'  # WiFi名称
password = '44444444' # WiFi密码

# MQTT设置
mqtt_broker = '117.78.16.25'   # MQTT服务器ip
#mqtt_broker = '192.168.43.78'  # 我的MQTT服务器ip

port = 1883  # 默认端口
client_id = '050707_phone'  # MQTT客户端ID
client_pwd = '0147852369'
#client_pwd = 'ab123456'

topic_send = b'$oc/devices/050707_zhihuixiaowu/sys/messages/up'
#topic_send = b'test_topic'  # 发布的主题
topic_rece = b'$oc/devices/050707_zhihuixiaowu/sys/messages/down'  # 订阅的主题
#topic_rece = b'test_mqtt'

#电机的两个引脚
INA =PWM(Pin(19,Pin.OUT),10000,0)#INA对应于IN+
INB =PWM(Pin(18,Pin.OUT),10000,2)#INB对应于IN-
INA.duty(0)
INB.duty(0)

#定义门的引脚
pwm_door = PWM(Pin(13))  
pwm_door.freq(50)
angle_open = 77
angle_close = 52
pwm_door.duty(angle_close)

#定义窗户的引脚
pwm_window = PWM(Pin(5))  
pwm_window.freq(50)
pwm_window.duty(22)

#定义是否手动关窗
global isWindowOpen
isWindowOpen = False

# 打开ADC并配置0-3.3V的范围 
adc=ADC(Pin(34))
adc.atten(ADC.ATTN_11DB)
adc.width(ADC.WIDTH_12BIT)

#定义红外传感器和顶部黄灯引脚
PIR = Pin(14, Pin.IN)
led_warn = Pin(12, Pin.OUT)

#关联DHT11与引脚(17).
DHT = dht.DHT11(machine.Pin(17))

#显示器
DEFAULT_I2C_ADDR = 0x27
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000) 
lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 2, 16)

#定义气体检测引脚
gas = Pin(23, Pin.IN, Pin.PULL_UP)

#定义按键引脚
button1 = Pin(16, Pin.IN, Pin.PULL_UP)
button2 = Pin(27, Pin.IN, Pin.PULL_UP)

#判断是否要输入密码
global isEnter
isEnter = False
global correctPassword
correctPassword = "-.-" #正确密码

#向客户端传输的数据
global tempuretor
global humidity
global isFire
global isRain
isRain = 0
global isDoorOpen
isDoorOpen = 0
global isCurtenOpen
isCurtenOpen = 0
global isLightOpen
isLightOpen = 0
global isFanOpen
isFanOpen = 0

#定义蜂鸣器
buzzer = PWM(Pin(25))
buzzer.duty(0)
arrMusic=[500,698,659,532,587,532]
def Alert_music():
    buzzer.duty(1000)
    for i in range(0,1):
        buzzer.freq(500)
        sleep(1)
    buzzer.duty(0)

#定义连接到新像素的引脚和led的数量.
pin_led = Pin(26, Pin.OUT)
np = neopixel.NeoPixel(pin_led, 4)
global lightCount
lightCount = 0

#亮度:0 - 255
brightness=100                                
colors=[[0,0,0],
        [brightness,0,0],                    #红
        [0,brightness,0],                    #绿
        [0,0,brightness],                    #蓝
        [brightness,brightness,0],           #黄
        [brightness,0,brightness],           #紫
        [0,brightness,brightness],           #粉
        [brightness,brightness,brightness]   #白
        ]     

#i2c config      读卡器设置
addr = 0x28
scl = 22
sda = 21
    
rc522 = mfrc522(scl, sda, addr)
rc522.PCD_Init()
rc522.ShowReaderDetails()  # 显示PCD - MFRC522读卡器详细信息

#从串口二中读取数据
uart = UART(2,9600)

#重置密码
def new_Password():
    global correctPassword
    count = 0
    time_count = 0
    correctPassword = ""
    while True:
        btnVal1 = button1.value()  # 读取按钮的值 1
        if(btnVal1 == 0):
            sleep_ms(10)
            while(btnVal1 == 0):
                time_count = time_count + 1  #开始计算按键被按下的时间
                sleep_ms(200)                #时间为200ms累加
                btnVal1 = button1.value()
                if(btnVal1 == 1):
                    count = count + 1
                    #print(count)
                    #print(time_count)
                    if(time_count > 3):      #如果按键被按下的时间大于200*3ms，新密码加"-"
                        lcd.clear()
                        #lcd.move_to(1, 1)
                        correctPassword = correctPassword + "-"
                    else:
                        lcd.clear()
                        correctPassword = correctPassword + "."  #否则加"."
                    lcd.putstr('{}'.format(correctPassword)) 
                    time_count = 0
                    
        btnVal2 = button2.value()
        if(btnVal2 == 0):
            lcd.clear()
            lcd.putstr('Set Successfully')
            time.sleep(1)
            break

#输入密码
def enter_Password():
    global isEnter
    count = 0
    time_count = 0
    password = ""   #输入密码
    attemp_count = 3
    while isEnter:
        btnVal1 = button1.value()  # 读取按钮的值 1
        if(btnVal1 == 0):
            sleep_ms(10)
            while(btnVal1 == 0):
                time_count = time_count + 1  #开始计算按键被按下的时间
                sleep_ms(200)                #时间为200ms累加
                btnVal1 = button1.value()
                if(btnVal1 == 1):
                    count = count + 1
                    #print(count)
                    #print(time_count)
                    if(time_count > 3):      #如果按键被按下的时间大于200*3ms，密码加"-"
                        lcd.clear()
                        #lcd.move_to(1, 1)
                        password = password + "-"
                    else:
                        lcd.clear()
                        password = password + "."  #否则加"."
                    lcd.putstr('{}'.format(password)) 
                    time_count = 0
                    
        btnVal2 = button2.value()
        if(btnVal2 == 0):
            if(password == correctPassword):  #如果输入密码正确
                lcd.clear()
                lcd.putstr("open")
                pwm_door.duty(angle_open)  #开门
                password = ""  #清除密码
                sleep_ms(1200)
                btnVal1 = button1.value()
                if(btnVal1 == 0):
                    lcd.move_to(0,1)
                    lcd.putstr("key up to change")
                    time.sleep(0.01)
                    while(btnVal1 == 0):
                        btnVal1 = button1.value()
                        if(btnVal1 == 1):
                            lcd.clear()
                            lcd.putstr("Set password")
                            lcd.move_to(0,1)
                            lcd.putstr("(2 to quit) 3...")
                            time.sleep(1)
                            if button2.value() == 0:
                                break
                            lcd.move_to(0,1)
                            lcd.putstr("(2 to quit) 2...")
                            time.sleep(1)
                            if button2.value() == 0:
                                break
                            lcd.move_to(0,1)
                            lcd.putstr("(2 to quit) 1...")
                            time.sleep(1)
                            if button2.value() == 0:
                                break
                            lcd.move_to(0,1)
                            lcd.putstr("(2 to quit) 0...")
                            time.sleep(1)
                            if button2.value() == 0:
                                break
                            lcd.clear()
                            lcd.putstr('Please Enter')
                            new_Password()
                            break
                else:
                    lcd.clear()
                    lcd.putstr("welcome")
                    time.sleep(2)
                    pwm_door.duty(angle_close)
                lcd.clear()
                isEnter = False
                break
            else:              #如果密码错误
                lcd.clear()
                lcd.putstr("error")
                pwm_door.duty(angle_close)  #关门
                sleep_ms(2000)
                lcd.clear()
                lcd.putstr("enter again")
                lcd.move_to(0, 1)
                attemp_count = attemp_count -1
                lcd.putstr('choice: {} time'.format(attemp_count))
                password = ""  #清除密码
                #print("attemp_count",attemp_count)
                if attemp_count <= 0:
                    lcd.clear()
                    lcd.putstr("Who Are You?")
                    pwm_door.duty(angle_close)
                    sleep(1)
                    lcd.clear()
                    isEnter = False
                    break

class MQTT_C():
    
    def __init__(self, mqtt_broker, client_id, port, client_pwd):
        
        self.client = MQTTClient(client_id, mqtt_broker, port, client_id, client_pwd, keepalive=60)
        
    # 连接wifi
    def connect_wifi(self ,ssid, password):
        wlan = network.WLAN(network.STA_IF)
        wlan.active(False)
        wlan.active(True)
        wlan.connect(ssid, password)
        # 等待连接
        while not wlan.isconnected(): 
            pass
        print("wifiOK")
    
    # 发布
    def send_topic(self,topic, msg):
        self.client.publish(topic, msg)
    
    # 订阅
    def sub_topic(self, sub):
        self.client.subscribe(sub)
    
    # 可在此处写功能函数, 可供回调函数调用
    #风扇开关控制
    def fan_start(self):
        global isFanOpen
        isFanOpen = 1
        INA.duty(0) #占空比范围为0-1023
        INB.duty(400)
    def fan_stop(self):
        global isFanOpen
        isFanOpen = 0
        INA.duty(0)
        INB.duty(0)
    #门开关控制
    def door_open(self):
        global isDoorOpen
        isDoorOpen = 1
        pwm_door.duty(angle_open)
    def door_close(self):
        global isDoorOpen
        isDoorOpen = 0
        pwm_door.duty(angle_close)
    #窗户开关控制
    def window_open(self):
        global isCurtenOpen
        isCurtenOpen = 1
        global isWindowOpen
        pwm_window.duty(77)
        isWindowOpen = True
    def window_close(self):
        global isCurtenOpen
        isCurtenOpen = 0
        global isWindowOpen
        pwm_window.duty(25)
        isWindowOpen = False
    #氛围灯开关控制
    def led_on(self):
        global isLightOpen
        isLightOpen = 1
        global lightCount
        ligthCount = 7
        print(lightCount)
        for j in range(0,4):
            np[j]=colors[7]
            np.write()
            time.sleep_ms(50)    
    def led_off(self):
        global isLightOpen
        isLightOpen = 0
        global lightCount
        lightCount = 0
        print(lightCount)
        for j in range(0,4):
            np[j]=colors[lightCount]
            np.write()
            time.sleep_ms(50)
    
    
    #红外传感
    def PIR_warnning(self):
        value = PIR.value()
        if value == 1:
            led_warn.value(1)# turn on led
        else:
            led_warn.value(0)
    #温湿度 
    def DH_mesure(self):
        global tempuretor
        global humidity
        DHT.measure() # 启动DHT11测量一次数据。
        tempuretor = DHT.temperature()
        humidity = DHT.humidity()
        #data={
        #        "temperator":temperature,
        #        "humidity":humidity
        #     }
        #client.send_topic(topic_send,json.dumps(data))
        lcd.move_to(10, 0)
        lcd.putstr('T= {}'.format(tempuretor))
        lcd.move_to(10, 1)
        lcd.putstr('H= {}'.format(humidity))
     
    #危险气体
    def gas_detective(self):
        global isFire
        gasVal = gas.value()  # 读取MQ-2的值
        lcd.move_to(0, 1)
        lcd.putstr('val: {}'.format(gasVal))
        if(gasVal == 1):
            isFire = 0
            #client.send_topic(topic_send,json.dumps({"isFire": 0}))
            #lcd.clear()
            lcd.move_to(0, 0)
            lcd.putstr('Safety   ')
        else:
            isFire = 1
            #client.send_topic(topic_send,json.dumps({"isFire": 1}))
            lcd.move_to(0, 0)
            lcd.putstr('dangerous')
            Alert_music()
     
    #摩斯密码门
    def Input_password(self):
        global isEnter
        btnVal1 = button1.value()  # 读取按钮1的值
        if(btnVal1 == 0):
            time.sleep(0.01)
            while(btnVal1 == 0):
                btnVal1 = button1.value()
                if(btnVal1 == 1):
                    isEnter = True
                    lcd.clear()
                    lcd.putstr("Enter  password")
                    lcd.move_to(0, 1)
                    lcd.putstr('choice: 3 time')
                    enter_Password()
    
    #下雨自动关窗
    def Auto_window(self):
        global isWindowOpen
        global isRain
        adcVal=adc.read()
        dacVal=adcVal//16
        voltage = adcVal / 4095.0 * 3.3
        if(voltage > 1):
            isRain = 1
        else:
            isRain = 0
        if isWindowOpen:
            if(voltage > 1):
                pwm_window.duty(25)
                isRain = 1
            else:
                pwm_window.duty(77)
                isRain = 0
    
    #按下右边按钮切换氛围灯颜色
    def Button_light(self):
        global lightCount
        btnVal2 = button2.value()
        if(btnVal2 == 0):
            time.sleep(0.01)
            lightCount = lightCount + 1    
            lightCount = lightCount % 8
            for j in range(0,4):
                np[j]=colors[lightCount]
                np.write()
                time.sleep_ms(50)
    
    #NFC开门
    def Door_card(self):
        card_data = 0
        if rc522.PICC_IsNewCardPresent():
            #print("Is new card present!")
            if rc522.PICC_ReadCardSerial() == True:
                print("Card UID:")
                #print(rc522.uid.uidByte[0 : rc522.uid.size])
                for i in rc522.uid.uidByte[0 : rc522.uid.size]:
                    card_data = card_data + i
            print(card_data)
            if(card_data == 561 or card_data == 407):
                pwm_door.duty(77)
                print("open")
                time.sleep(2)
                pwm_door.duty(52)
            else:
                print("error")
            card_data = 0
    
    #向客户端发送信息
    def Send_msg(self):
        global tempuretor
        global humidity
        global isFire
        global isRain
        global isDoorOpen
        global isCurtenOpen
        global isLightOpen
        global isFanOpen
        data={
                "deviceId": 050702,
                "name": "test",
                "temperator": tempuretor,
                "humidity": humidity,
                "isFire": isFire,
                "isRain": isRain,
                "doorStatus": isDoorOpen,
                "curtainStatus": isCurtenOpen,
                "lightStatus": isLightOpen,
                "fanStatus": isFanOpen,
             }
        client.send_topic(topic_send,json.dumps(data))
        
    #实时更新
    def real_time_update(self):
        self.Door_card()
        self.Auto_window()
        self.Button_light()
        self.PIR_warnning()
        self.gas_detective()
        self.DH_mesure()
        self.Input_password()
    
    # 回调
    def callback_mqtt(self,topic, msg):
        
        #resjson=msg.decode('utf-8')
        #data = json.loads(resjson)
        #msg =(data['message'])
        print(msg)
        
        if msg == b'{"fanStatus":1}':
            print("风扇开了")
            self.fan_start()
        elif msg == b'{"fanStatus":0}':
            print("风扇关了")
            self.fan_stop()
        elif msg == b'{"doorStatus":1}':
            print("门开了")
            self.door_open()
        elif msg == b'{"doorStatus":0}':
            print("门关了")
            self.door_close()
        elif msg == b'{"curtainStatus":1}':
            print("窗开了")
            self.window_open()
        elif msg == b'{"curtainStatus":0}':
            print("窗关了")
            self.window_close()
        elif msg == b'{"lightStatus":1}':
            print('灯开了')
            self.led_on()
        elif msg == b'{"lightStatus":0}':
            print('灯关了')
            self.led_off()
        
    #语音控制
    def sound_msg(self):
        data = uart.read()
        if data:
            print(data)
            if data == b'\x07':
                global isFanOpen
                isFanOpen = 1
                print("语音风扇开了")
                self.fan_start()
            elif data == b'\n':
                global isFanOpen
                isFanOpen = 0
                print("语音风扇关了")
                self.fan_stop()
            elif data == b'\x03':
                global isDoorOpen
                isDoorOpen = 1
                print("语音门开了")
                self.door_open()
            elif data == b'\x04':
                global isDoorOpen
                isDoorOpen = 0
                print("语音门关了")
                self.door_close()
            elif data == b'\x05':
                global isCurtenOpen
                isCurtenOpen = 1
                print("语音窗开了")
                self.window_open()
            elif data == b'\x06':
                global isCurtenOpen
                isCurtenOpen = 0
                print("语音窗关了")
                self.window_close()
            elif data == b'\x01':
                global isLightOpen
                isLightOpen = 1
                print('语音灯开了')
                self.led_on()
            elif data == b'\x02':
                global isLightOpen
                isLightOpen = 0
                print('语音灯关了')
                self.led_off()
    
    def connect_mqtt(self):
        print("connecting...")
        # 连接到MQTT代理
        self.client.connect()
        self.client.set_callback(self.callback_mqtt)
        print("success")
        
    def rece_data(self):
        try:
            while True:
                self.real_time_update()
                time.sleep(0.19)
                #检测客户端是否有信息
                self.client.check_msg()
                self.sound_msg()
                self.Send_msg()
                time.sleep(0.19)
        except:
            INA.duty(0)
            INB.duty(0)
            INA.deinit()
            INB.deinit()
            buzzer.deinit()
            uart.deinit()

print('Running...')
client = MQTT_C(mqtt_broker,client_id,port,client_pwd)#实例化对象
print('Try connect Wifi...')
client.connect_wifi(ssid,password) # 连接WiFi
print('Try connect mqtt')
client.connect_mqtt() # 连接mqtt
#client.send_topic(topic_send,json.dumps({"deviceId": 050702,"name": "test"})) # 消息发布
client.sub_topic(topic_rece) # 消息订阅
client.rece_data()


